#include <sys/types.h>
#include <unistd.h>
#include "common.hpp"
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>

//setsid()建立会话的前提: 调用进程不能说一个进程组的组长进程
//解决方法如下: fork()出一个子进程, 让子进程去setsid(), 因为子进程不可能是组长


const char* root = "/";
const char* dev_null = "/dev/null"; //凡是写入这个文件的内容都被丢弃; 凡是从这个文件读取的内容都为空


//如果nochdir = 0, daemon()将进程的当前工作目录改为根目录("/"); 否则, 保持当前工作目录不变
//如果noclose = 0, daemon()将stdin, stdout, stderr重定向到/dev/null; 否则, 不改变这些文件描述符

void Daemon(int nochdir, int noclose)
{
    //1. 忽略可能引起程序异常退出的信号
    signal(SIGCHLD, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);

    //2. 让自己不要成为组长 -- 子进程去做事, 父进程退出
    pid_t id = fork();
    if(id < 0)
    {
        LOG(wzx::log_level::FATAL) << "fork fail!!";
        exit(FORK_ERR);
    }
    else if(id == 0)//子
    {
        if(nochdir == 0)
            chdir("/");

        //3. 建立新会话
        setsid();

        if(noclose == 0)
        {
            //4. 断开与stdin, stdout, stderr的关联, 因为server不需要和从键盘读取信息, 也不需要向显示器写入信息
            // 法1(不推荐): 因为server的代码可能会对0,1,2文件进行IO操作, 直接close会导致对一个文件描述符不存在的文件进行IO了, 会报错
            // close(1);close(2);close(3); 
            // 法2:
            int fd = open(dev_null, O_RDWR);
            if(fd < 0)
            {
                LOG(wzx::log_level::FATAL) << "open dev_null fail!!";
                exit(OPEN_ERR);
            }
            dup2(fd, 0);
            dup2(fd, 1);
            dup2(fd, 2);
        }
    
    }
    else//父
    {
        exit(NORMAL);
    }
}